#include <dspSimulator/instruction//mvc.h>

bool MVC::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void MVC::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);
    // --------------------------------- MVC .S2 unit Operands when moving from the control file to the register file: ---------------------------------------------
    {
        llvm::Function *mvc = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "MVCKind1",
                                                     module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", mvc);
        irBuilder->SetInsertPoint(entryBB);

        auto cRegPtr = getCRegPtr(module, irBuilder);
        auto cReg = irBuilder->CreateLoad(cRegPtr);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);

        irBuilder->CreateStore(cReg, dstRegPtr);
        irBuilder->CreateRetVoid();
    }

    // --------------------------------- MVC .S2 unit Operands when moving from the register file to the control ---------------------------------------------
    {
        llvm::Function *mvc = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "MVCKind2",
                                                     module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", mvc);
        irBuilder->SetInsertPoint(entryBB);

        auto cRegPtr = getCRegPtr(module, irBuilder);
        auto src2RegPtr = getSrc2LoRegPtr(module, irBuilder);
        auto src2Reg = irBuilder->CreateLoad(src2RegPtr);

        irBuilder->CreateStore(src2Reg, cRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::shared_ptr<Instruction> MVC::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<MVC> mvc;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        // Operands when moving from the control file to the register
        if (checkBits<10>(data, 2, 0b0011111000) && existStandardCondition(data)) {
            mvc = std::make_shared<MVC>(dspInstInfo->getAddress(), baseCycle);
            mvc->creg_z = extractBits<4>(data, 28);
            mvc->dst = extractBits<5>(data, 23).to_ulong();
            mvc->crlo = extractBits<5>(data, 18);
            mvc->crhi = extractBits<5>(data, 13);
            mvc->x = data[12];
            mvc->s = data[1];
            mvc->p = data[0];
            mvc->funcUnit = FuncUnit::S2;
            mvc->instFormat = InstFormat::Kind1;
        } else if (checkBits<10>(data, 2, 0b0011101000) && existStandardCondition(data)) {
            // Operands when moving from the register file to the control
            mvc = std::make_shared<MVC>(dspInstInfo->getAddress(), baseCycle);
            mvc->creg_z = extractBits<4>(data, 28);
            mvc->crlo = extractBits<5>(data, 23);
            mvc->src2 = extractBits<5>(data, 18).to_ulong();
            mvc->crhi = extractBits<5>(data, 13);
            mvc->x = data[12];
            mvc->s = data[1];
            mvc->funcUnit = FuncUnit::S2;
            mvc->p = data[0];
            mvc->instFormat = InstFormat::Kind2;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (checkBits<6>(data, 10, 0b110110) && checkBits<6>(data, 1, 0b110111)) {
            mvc = std::make_shared<MVC>(dspInstInfo->getAddress(), baseCycle);
            mvc->isCompactInstruction = true;
            mvc->compactInstKind = "Sx1";
            mvc->src2 = extractBits<3>(data, 7).to_ulong();
            mvc->crlo = 0b01101;
            mvc->crhi = 0b00000;
            mvc->s = data[0];
            mvc->PROT = dspInstInfo->getPROT();
            mvc->RS = dspInstInfo->getRS();
            mvc->BR = dspInstInfo->getBR();
            mvc->SAT = dspInstInfo->getSAT();
            mvc->DSZ = dspInstInfo->getDSZ();
            mvc->p = dspInstInfo->getP();
            mvc->fixUpRegOnCompactInstruction();
            mvc->funcUnit = FuncUnit::S2;
            mvc->instFormat = InstFormat::Kind2;
        }
    }
    return mvc;
}

std::string MVC::toString() const {
    if (instFormat == InstFormat::Kind1)
        return getStandardConditionString() + " " + m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " +
               CRegName[static_cast<u32>(getCRegKindByCrHiAndCrLo())] + "," + getDstReg32Name();
    else
        return getStandardConditionString() + " " + m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " +
               getSrc2Reg32Name() + "," + CRegName[static_cast<u32>(getCRegKindByCrHiAndCrLo())];
}

void MVC::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {

}

std::shared_ptr<DspInstruction> MVC::clone() {
    return std::make_shared<MVC>(*this);
}

